package com.aikon.wht.multiThread;

import com.google.common.util.concurrent.*;
import com.google.errorprone.annotations.concurrent.GuardedBy;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * @author haitao.wang
 */
public class TestOfDirectExecutorService {

    public static void main(String[] args) {
        TestOfDirectExecutorService test = new TestOfDirectExecutorService();
        test.testRun();

    }

    public void testRun() {
        DirectExecutorService service = new DirectExecutorService();

        for (int i = 0; i < 10; i++) {
            int finalI = i;
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " running");
                }
            };

            new Thread(new Runner(service, runnable)).start();

//            ListenableFuture future = service.submit(runnable,1000000000);
//            Futures.addCallback(future, new FutureCallback<Object>() {
//                @Override
//                public void onSuccess(Object o) {
//                    System.out.println("success " + o);
//                }
//
//                @Override
//                public void onFailure(Throwable throwable) {
//                    System.out.println("fail " + throwable);
//                }
//            },MoreExecutors.newDirectExecutorService());
        }
        System.out.println("main end");
    }

    public static class Runner implements Runnable {
        DirectExecutorService service;

        Runnable runnable;

        public Runner(DirectExecutorService service) {
            this.service = service;
        }

        public Runner(DirectExecutorService service, Runnable runnable) {
            this.service = service;
            this.runnable = runnable;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " " + "running" + service.getRunningTasks());
            service.submit(runnable);
            System.out.println("--------------");
            System.out.println(service.isShutdown());
            System.out.println(service.isTerminated());
        }
    }


    private static final class DirectExecutorService extends AbstractListeningExecutorService {
        /** Lock used whenever accessing the state variables (runningTasks, shutdown) of the executor */
        private final Object lock = new Object();

        /*
         * Conceptually, these two variables describe the executor being in
         * one of three states:
         *   - Active: shutdown == false
         *   - Shutdown: runningTasks > 0 and shutdown == true
         *   - Terminated: runningTasks == 0 and shutdown == true
         */
        @GuardedBy("lock")
        private int runningTasks = 0;

        @GuardedBy("lock")
        private boolean shutdown = false;

        @Override
        public void execute(Runnable command) {
            startTask();
            try {
                command.run();
            } finally {
                endTask();
            }
        }

        @Override
        public boolean isShutdown() {
            synchronized (lock) {
                return shutdown;
            }
        }

        @Override
        public void shutdown() {
            synchronized (lock) {
                shutdown = true;
                if (runningTasks == 0) {
                    lock.notifyAll();
                }
            }
        }

        // See newDirectExecutorService javadoc for unusual behavior of this method.
        @Override
        public List<Runnable> shutdownNow() {
            shutdown();
            return Collections.emptyList();
        }

        @Override
        public boolean isTerminated() {
            synchronized (lock) {
                return shutdown && runningTasks == 0;
            }
        }

        @Override
        public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
            long nanos = unit.toNanos(timeout);
            synchronized (lock) {
                while (true) {
                    if (shutdown && runningTasks == 0) {
                        return true;
                    } else if (nanos <= 0) {
                        return false;
                    } else {
                        long now = System.nanoTime();
                        TimeUnit.NANOSECONDS.timedWait(lock, nanos);
                        nanos -= System.nanoTime() - now; // subtract the actual time we waited
                    }
                }
            }
        }

        /**
         * Checks if the executor has been shut down and increments the running task count.
         *
         * @throws RejectedExecutionException if the executor has been previously shutdown
         */
        private void startTask() {
            synchronized (lock) {
                if (shutdown) {
                    throw new RejectedExecutionException("Executor already shutdown");
                }
                runningTasks++;
            }
        }

        /** Decrements the running task count. */
        private void endTask() {
            synchronized (lock) {
                int numRunning = --runningTasks;
                if (numRunning == 0) {
                    lock.notifyAll();
                }
            }
        }

        public int getRunningTasks() {
            synchronized (lock) {
                return runningTasks;
            }
        }
    }
}
